import TouchDot from "./TouchDot"
import { PieceType, PlayerType } from "./GameDefine"
import BasePiece from "./Pieces/BasePiece"
/**
 * 走法规则检验器
 */
export default class Algorithm {

    /** 棋盘的二位数组,包含了所有棋子布局 */
    boarddots: TouchDot[][] = [];
    /** 执棋者 */
    playerType: PlayerType = PlayerType.RED;

    /** 检测棋子移动 */
    public checkPieceMove(opDot: TouchDot, tgDot: TouchDot): boolean {
        if (!opDot || !tgDot || !opDot.piece) {
            return false
        }
        //如果目标有自己的棋子不能移动
        if (tgDot.piece && tgDot.piece.playerType === opDot.piece.playerType) {
            return false
        }

        switch (opDot.piece.pieceType) {
            case PieceType.KING://将/帅
                return this.checkKingMove(opDot, tgDot)
            case PieceType.MANDARIN://士/仕
                return this.checkMandarinMove(opDot, tgDot)
            case PieceType.ELEPHANT://象/相
                return this.checkElephantMove(opDot, tgDot)
            case PieceType.ROOKS://车
                return this.checkRooksMove(opDot, tgDot)
            case PieceType.KNIGHTS://马
                return this.checkKnightsMove(opDot, tgDot)
            case PieceType.CANNONS://炮
                return this.checkCannonsMove(opDot, tgDot)
            case PieceType.PAWNS://兵
                return this.checkPawnsMove(opDot, tgDot)
            default:
                return false
        }
    }

    /** 检测 将军 的走法 */
    private checkKingMove(opDot: TouchDot, tgDot: TouchDot): boolean {
        let piece: BasePiece = opDot.piece
        let oIndex: cc.Vec3 = opDot.index
        let tIndex: cc.Vec3 = tgDot.index
        let oColor: PlayerType = piece.playerType

        //一次只能移动一格
        let mDir = oIndex.sub(tIndex)
        if (1 != (Math.abs(mDir.x) + Math.abs(mDir.y))) {
            return false
        }

        //将的移动范围
        //y: 3-5
        if (tIndex.y < 3 || tIndex.y > 5) {
            return false
        }
        if (oColor === this.playerType) {
            //我方只能在
            //x: 0-2
            if (tIndex.x < 0 || tIndex.x > 2) {
                return false
            }
        }
        else {
            //对方只能在
            //x: 7-9
            if (tIndex.x < 7 || tIndex.x > 9) {
                return false
            }
        }

        return true
    }
    /** 检测 士 的走法*/
    private checkMandarinMove(opDot: TouchDot, tgDot: TouchDot): boolean {
        let piece: BasePiece = opDot.piece
        let oIndex: cc.Vec3 = opDot.index
        let tIndex: cc.Vec3 = tgDot.index
        let oColor: PlayerType = piece.playerType

        //一次只能沿斜线移动一格
        let mDir = oIndex.sub(tIndex)
        if (1 != Math.abs(mDir.x) || 1 != Math.abs(mDir.y)) {
            return false
        }

        //士的移动范围
        //y: 3-5
        if (tIndex.y < 3 || tIndex.y > 5) {
            return false
        }
        if (oColor === this.playerType) {
            //我方只能在
            //x: 0-2
            if (tIndex.x < 0 || tIndex.x > 2) {
                return false
            }
        }
        else {
            //对方只能在
            //x: 7-9
            if (tIndex.x < 7 || tIndex.x > 9) {
                return false
            }
        }

        return true
    }
    /**
     * 校验 相/象
     * @param opDot 起手点
     * @param tgDot 落棋点
     * @returns 
     */
    private checkElephantMove(opDot: TouchDot, tgDot: TouchDot): boolean {
        let piece: BasePiece = opDot.piece
        let oIndex: cc.Vec3 = opDot.index
        let tIndex: cc.Vec3 = tgDot.index
        let oColor: PlayerType = piece.playerType

        //象只能走田字
        let mDir = oIndex.sub(tIndex)
        if (2 != Math.abs(mDir.x) || 2 != Math.abs(mDir.y)) {
            return false
        }

        //象不能过河
        if (oColor === this.playerType) {
            //我方只能在
            //x: 0-4
            if (tIndex.x > 4) {
                return false
            }
        }
        else {
            //对方只能在
            //x: 5-9
            if (tIndex.x < 5) {
                return false
            }
        }

        //田中间有棋子不能走
        let midIndex = cc.v3(mDir.x / 2, mDir.y / 2)
        midIndex = oIndex.sub(midIndex)
        if (midIndex.x < 0 || midIndex.y < 0 || this.boarddots[midIndex.x][midIndex.y].piece) {
            return false
        }

        return true
    }
    /**
         * 校验 相/象
         * @param opDot 起手点
         * @param tgDot 落棋点
         * @returns
         */
    private checkRooksMove(opDot: TouchDot, tgDot: TouchDot): boolean {
        let oIndex: cc.Vec3 = opDot.index
        let tIndex: cc.Vec3 = tgDot.index

        //车只能走直线
        let mDir = oIndex.sub(tIndex)
        if (0 != mDir.x && 0 != mDir.y) {
            return false
        }

        //路线上不能有棋子
        let lineIndex = mDir.clone()
        while (true) {
            lineIndex.x = lineIndex.x == 0 ? 0 : lineIndex.x > 0 ? --lineIndex.x : ++lineIndex.x
            lineIndex.y = lineIndex.y == 0 ? 0 : lineIndex.y > 0 ? --lineIndex.y : ++lineIndex.y
            if (0 == lineIndex.x && 0 == lineIndex.y) {
                break
            }

            let newIndex = oIndex.sub(lineIndex)
            if (this.boarddots[newIndex.x][newIndex.y].piece) {
                return false
            }
        }

        return true
    }
    /**
     * 校验 相/象
     * @param opDot 起手点
     * @param tgDot 落棋点
     * @returns
     */
    private checkKnightsMove(opDot: TouchDot, tgDot: TouchDot): boolean {
        let oIndex: cc.Vec3 = opDot.index
        let tIndex: cc.Vec3 = tgDot.index

        //马只能走日
        let mDir = oIndex.sub(tIndex)
        if (3 != (Math.abs(mDir.x) + Math.abs(mDir.y))
            || 2 != Math.abs(mDir.x) && 2 != Math.abs(mDir.y)) {
            return false
        }

        //不能撇脚
        let newIndex = mDir.clone()
        newIndex.x /= 2
        newIndex.y /= 2
        if (Math.abs(newIndex.x) < 1) {
            newIndex.x = 0
        }
        if (Math.abs(newIndex.y) < 1) {
            newIndex.y = 0
        }
        newIndex = oIndex.sub(newIndex)

        if (this.boarddots[newIndex.x][newIndex.y].piece) {
            return false
        }

        return true
    }
    /**
     * 校验 相/象
     * @param opDot 起手点
     * @param tgDot 落棋点
     * @returns
     */
    private checkCannonsMove(opDot: TouchDot, tgDot: TouchDot): boolean {
        let oIndex: cc.Vec3 = opDot.index
        let tIndex: cc.Vec3 = tgDot.index

        //炮只能走直线
        let mDir = oIndex.sub(tIndex)
        if (0 != mDir.x && 0 != mDir.y) {
            return false
        }

        //统计路线上棋子数量
        let pieceCount: number = 0
        let lineIndex = mDir.clone()
        while (true) {
            lineIndex.x = lineIndex.x == 0 ? 0 : lineIndex.x > 0 ? --lineIndex.x : ++lineIndex.x
            lineIndex.y = lineIndex.y == 0 ? 0 : lineIndex.y > 0 ? --lineIndex.y : ++lineIndex.y
            if (0 == lineIndex.x && 0 == lineIndex.y) {
                break
            }

            let newIndex = oIndex.sub(lineIndex)
            if (this.boarddots[newIndex.x][newIndex.y].piece) {
                ++pieceCount
            }
        }

        //如果目标点有敌方的棋子，那么必须中间要有一个棋子,反之中间不能有棋子
        return tgDot.piece ? (1 == pieceCount) : (0 == pieceCount)
    }
    /**
     * 校验 兵/卒
     * @param opDot 起手点
     * @param tgDot 落棋点
     * @returns
     */
    private checkPawnsMove(opDot: TouchDot, tgDot: TouchDot): boolean {
        let piece: BasePiece = opDot.piece
        let oIndex: cc.Vec3 = opDot.index
        let tIndex: cc.Vec3 = tgDot.index
        let oColor: PlayerType = piece.playerType

        //兵一次只能走一格
        let mDir = oIndex.sub(tIndex)
        if (1 != (Math.abs(mDir.x) + Math.abs(mDir.y))) {
            return false
        }

        //在各自地盘只能往前走
        if (oColor === this.playerType) {
            //自己方
            if (oIndex.x > tIndex.x) {
                return false
            }
            //没过河之前不能横着走
            if (oIndex.x < 5 && mDir.y != 0) {
                return false
            }
        }
        else {
            //对方
            if (oIndex.x < tIndex.x) {
                return false
            }

            //没过河之前不能横着走
            if (oIndex.x > 4 && mDir.y != 0) {
                return false
            }
        }

        return true
    }
}
